#include "uprocess.h"
#include "fs.h"
#include "str.h"
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <limits.h>
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <signal.h>

#define MAX_ARGVP_SIZE     (10)
#define MAX_ENVPP_SIZE     (200)

extern char **environ;

namespace log4x
{

uprocess::uprocess()
{
}

uprocess::~uprocess()
{
}

int
uprocess::pid()
{
    return (int)getpid();
}

int
uprocess::pids(const string& pname, vector<int>& pids, vector<string>& cmdLines)
{
    char cmd[128] = {'\0'};

    snprintf(cmd, sizeof(cmd), "pidof %s", pname.c_str());
    string buf;
    if (this->system(cmd, buf) < 0)
    {
        return -1;
    }

    vector<string> v;
    str::split(buf, " ", v);
    if (v.empty())
    {
        return -1;
    }

    for (size_t i = 0; i < v.size(); ++i)
    {
        int pid = str::to_int(v[i]);
        pids.push_back(pid);
        cmdLines.push_back(cmdline(pid));
    }

    return 0;
}

int
uprocess::pids(const string& workdir, map<int, string>& procs)
{
    string result;
    if (this->system("pgrep ''", result) < 0)
    {
        return -1;
    }

    vector<string> v;
    str::split(result, "\n", v);

    vector<int> pids;
    for (size_t i = 0; i < v.size(); ++i)
    {
        pids.push_back(str::to_int(v[i]));
    }

    for (size_t i = 0; i < pids.size(); ++i)
    {
        if (pids[i] == this->pid())
        {
            continue;
        }

        string ppath   = uprocess::ppath(pids[i]);
        string abspath = fs::absolute_path(workdir);
        if (ppath.empty() || fs::shortname(ppath).find(abspath) == string::npos)
        {
            continue;
        }

        procs[pids[i]] = this->ppath(pids[i]);
    }
    return 0;
}

string
uprocess::ppath(int pid)
{
    char   filename[512] = {0};
    int    size          = 511;
    int    bytes         = 0;
    string proc          = "/proc/";

    if (-1 == pid)
    {
        proc += "self";
    }
    else
    {
        proc += str::to_str(pid);
    }

    string path;
    bytes = (int)readlink((proc + "/exe").c_str(), filename, size);
    if (bytes > 0)
    {
        filename[bytes < size - 1 ? bytes : size - 1] = '\0';
        path = filename;
    }
    else
    {
        /**
         * 
         * 对于内核版本比较低的系统readlink由go语言编写的程序会失败,在这里做特殊处理
         * redhat6系列(2.6.32-754) 
         *
         * 执行命令错误提示:
         * ll /proc/pid/exe 
         * ls: cannot read symbolic link /proc/14823/exe: No such file or directory
         */
        bytes = (int)readlink((proc + "/cwd").c_str(), filename, size);
        if (bytes < 0)
        {
            return "";
        }

        string cmdline;
        str::read_from_file(proc + "/cmdline", cmdline);
        if (cmdline.empty())
        {
            return "";
        }

        cmdline.erase(cmdline.end() - 1);
        path = string(filename) + "/" + cmdline;
        path = fs::absolute_path(path);
    }
    return path;
}

int
uprocess::system(const string& path_cmd)
{
    string cmd = path_cmd;
    fs::path_to_unix(cmd);

    ::system(cmd.c_str());
    return 0;
}

int
uprocess::system(const string& path_cmd, string& result)
{
    string cmd = path_cmd;
    fs::path_to_unix(cmd);

    char  buffer[1014] = {0};
    FILE* fp = NULL;

    /* Run DIR so that it writes its output to a pipe. Open this
     * pipe with read text attribute so that we can read it
     * like a text file.
     */
    if (!(fp = popen(cmd.c_str(), "r")))
    {
        return -1;
    }

    /* Read pipe until end of file, or an error occurs. */
    while (fgets(buffer, 1014, fp))
    {
        result += buffer;
    }

    /* Close pipe and print return value of fp. */
    if (feof(fp))
    {
        pclose(fp);
    }
    else
    {
        pclose(fp);
        return -1;
    }
    return 0;
}

string
uprocess::cmdline(int pid)
{
    if (-1 == pid)
    {
        pid = this->pid();
    }
    string result;
    string path = str::format("/proc/%d/cmdline", pid);

    if (str::read_from_file(path, result) < 0)
    {
        return "";
    }

    if (result.empty())
    {
        return "";
    }

    for (size_t i = 0; i < result.size() - 1; ++i)
    {
        if (result[i] == 0)
        {
            result[i] = ' ';
        }
    }

    result.erase(result.end() - 1);
    return result;
}

}
